from flask import Blueprint, request, jsonify, current_app
import os
import json
from services.git_service import GitService
from services.auth_service import auth_required
from utils.db import get_db
import psycopg2.extras
import re

git_bp = Blueprint('git', __name__, url_prefix='/api/git')

def get_project_credentials(project_id=None, repo_url=None):
    """从数据库获取项目的Git凭证
    
    可以通过项目ID或仓库URL获取
    """
    try:
        current_app.logger.info(f"尝试获取项目凭证: project_id={project_id}, repo_url={repo_url}")
        conn = get_db(current_app)
        
        # 使用正确的cursor_factory
        cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
        
        query = ""
        params = ()
        
        if project_id:
            query = """
                SELECT id, git_repository_url, git_username, git_password 
                FROM projects WHERE id = %s
            """
            params = (project_id,)
            current_app.logger.info(f"通过项目ID查询: {project_id}")
        elif repo_url:
            # 清理URL中可能存在的凭证部分，以便正确匹配数据库中的URL
            cleaned_url = repo_url
            if '@' in repo_url and '://' in repo_url:
                protocol, rest = repo_url.split('://', 1)
                if '@' in rest:
                    _, host_path = rest.split('@', 1)
                    cleaned_url = f"{protocol}://{host_path}"
                    current_app.logger.info(f"已清理URL中的凭证部分用于查询")
            
            query = """
                SELECT id, git_repository_url, git_username, git_password 
                FROM projects WHERE git_repository_url = %s
            """
            params = (cleaned_url,)
            current_app.logger.info(f"通过仓库URL查询: {cleaned_url}")
        else:
            current_app.logger.warning("未提供项目ID或仓库URL，无法获取凭证")
            return None
        
        # 执行查询    
        cursor.execute(query, params)
        project = cursor.fetchone()
        cursor.close()
        
        if project:
            current_app.logger.info(f"找到项目记录: ID={project['id']}")
            
            # 检查凭证是否完整
            if project.get('git_username') and project.get('git_password'):
                current_app.logger.info(f"找到完整的Git凭证")
                return {
                    'username': project['git_username'],
                    'password': project['git_password']
                }
            else:
                current_app.logger.warning(f"项目记录存在，但Git凭证不完整")
                return None
        else:
            current_app.logger.warning(f"未找到匹配的项目记录")
            return None
    except Exception as e:
        current_app.logger.error(f"获取项目凭证失败: {str(e)}")
        # 记录详细的错误信息便于调试
        import traceback
        current_app.logger.error(traceback.format_exc())
        return None

@git_bp.route('/status', methods=['POST'])
@auth_required
def git_status():
    """获取Git仓库状态"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_status(repo_path, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"获取Git状态失败: {str(e)}")
        return jsonify({"success": False, "message": f"获取Git状态失败: {str(e)}", "data": None}), 500

@git_bp.route('/add-all', methods=['POST'])
@auth_required
def git_add_all():
    """添加所有更改到暂存区"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_add_all(repo_path, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"Git add操作失败: {str(e)}")
        return jsonify({"success": False, "message": f"Git add操作失败: {str(e)}", "data": None}), 500

@git_bp.route('/commit', methods=['POST'])
@auth_required
def git_commit():
    """提交更改"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        message = data.get('message')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        if not message:
            return jsonify({"success": False, "message": "提交信息不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_commit(repo_path, message, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"Git commit操作失败: {str(e)}")
        return jsonify({"success": False, "message": f"Git commit操作失败: {str(e)}", "data": None}), 500

@git_bp.route('/commit-all', methods=['POST'])
@auth_required
def git_commit_all():
    """添加所有更改并提交"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        message = data.get('message')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        if not message:
            return jsonify({"success": False, "message": "提交信息不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_commit_all(repo_path, message, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"Git commit-all操作失败: {str(e)}")
        return jsonify({"success": False, "message": f"Git commit-all操作失败: {str(e)}", "data": None}), 500

@git_bp.route('/push', methods=['POST'])
@auth_required
def git_push():
    """推送更改到远程仓库"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        remote = data.get('remote', 'origin')
        branch = data.get('branch')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_push(repo_path, remote, branch, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"Git push操作失败: {str(e)}")
        return jsonify({"success": False, "message": f"Git push操作失败: {str(e)}", "data": None}), 500

@git_bp.route('/pull', methods=['POST'])
@auth_required
def git_pull():
    """从远程仓库拉取更改"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        remote = data.get('remote', 'origin')
        branch = data.get('branch')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_pull(repo_path, remote, branch, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"Git pull操作失败: {str(e)}")
        return jsonify({"success": False, "message": f"Git pull操作失败: {str(e)}", "data": None}), 500

@git_bp.route('/branch', methods=['POST'])
@auth_required
def git_branch():
    """列出所有分支"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_branch(repo_path, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"获取Git分支失败: {str(e)}")
        return jsonify({"success": False, "message": f"获取Git分支失败: {str(e)}", "data": None}), 500

@git_bp.route('/checkout', methods=['POST'])
@auth_required
def git_checkout():
    """切换分支"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        branch = data.get('branch')
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        if not branch:
            return jsonify({"success": False, "message": "分支名不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_checkout(repo_path, branch, credentials)
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"Git checkout操作失败: {str(e)}")
        return jsonify({"success": False, "message": f"Git checkout操作失败: {str(e)}", "data": None}), 500

@git_bp.route('/log', methods=['POST'])
@auth_required
def git_log():
    """获取提交历史"""
    try:
        data = request.get_json()
        repo_path = data.get('repo_path')
        count = data.get('count', 10)
        project_id = data.get('project_id')
        
        if not repo_path:
            return jsonify({"success": False, "message": "仓库路径不能为空", "data": None}), 400
        
        # 获取项目凭证
        credentials = get_project_credentials(project_id=project_id) if project_id else None
        
        result = GitService.git_log(repo_path, count, credentials)
        
        if result["success"]:
            # 解析日志结果为结构化数据
            log_lines = result["data"]["stdout"].strip().split('\n')
            log_entries = []
            
            for line in log_lines:
                if line:
                    parts = line.split('|', 3)
                    if len(parts) >= 4:
                        entry = {
                            "hash": parts[0],
                            "author": parts[1],
                            "date": parts[2],
                            "message": parts[3]
                        }
                        log_entries.append(entry)
            
            result["data"]["log_entries"] = log_entries
            
        return jsonify(result)
    except Exception as e:
        current_app.logger.error(f"获取Git日志失败: {str(e)}")
        return jsonify({"success": False, "message": f"获取Git日志失败: {str(e)}", "data": None}), 500

@git_bp.route('/clone', methods=['POST'])
@auth_required
def git_clone():
    """克隆Git仓库到本地
    
    请求数据:
    {
        "repo_url": "仓库URL",
        "project_folder": "可选的目标文件夹路径",
        "project_id": "可选的项目ID"
    }
    """
    try:
        # 获取请求数据
        data = request.json
        repo_url = data.get('repo_url')
        project_folder = data.get('project_folder')
        project_id = data.get('project_id')
        
        current_app.logger.info(f"收到克隆请求: {repo_url} 到 {project_folder}")
        
        # 验证URL
        if not repo_url:
            current_app.logger.error("未提供仓库URL")
            return jsonify({
                "success": False,
                "message": "必须提供仓库URL",
                "data": None
            })
        
        # 清理URL格式，确保一致性
        if repo_url.endswith('/'):
            repo_url = repo_url[:-1]
            current_app.logger.info("移除URL末尾的斜杠")
            
        # 确保.git后缀存在
        if not repo_url.endswith('.git') and 'gitee.com' in repo_url:
            repo_url = repo_url + '.git'
            current_app.logger.info("添加.git后缀到URL")
            
        # 创建包含凭证的URL (如果需要的话)
        url_with_credentials = repo_url
        
        # 凭证处理
        credentials = None
        
        # 基于仓库URL确定使用的凭证类型
        if 'gitee.com' in repo_url:
            current_app.logger.info("检测到Gitee仓库，尝试使用访问令牌")
            
            # 从环境变量或配置中获取Gitee访问令牌
            gitee_token = os.environ.get('GITEE_ACCESS_TOKEN', '')
            
            if not gitee_token:
                # 如果环境变量中没有设置，尝试从配置文件读取
                gitee_token = current_app.config.get('GITEE_ACCESS_TOKEN', '')
                
            if not gitee_token:
                # 使用硬编码的令牌（注意：在生产环境中应该使用更安全的方式）
                gitee_token = "your_actual_gitee_token_here"  # 请替换为实际的访问令牌
                current_app.logger.warning("未找到环境变量中的Gitee令牌，使用硬编码值")
            
            credentials = {
                'username': 'lvwanwan',
                'password': gitee_token,
                'is_token': True  # 标记这是一个访问令牌
            }
            
            current_app.logger.info(f"使用Gitee访问令牌进行认证")
        else:
            # 对于非Gitee仓库，使用标准Git凭证
            credentials = {
                'username': 'lvwanwan',
                'password': 'your_password_here',  # 请替换为实际密码
                'is_token': False
            }
            current_app.logger.info(f"使用标准Git凭证: 用户名={credentials['username']}")
        
        # 清理URL中已存在的凭证
        if '@' in repo_url and '://' in repo_url:
            protocol, rest = repo_url.split('://', 1)
            if '@' in rest:
                # URL已包含凭证，提取主机部分
                host_path = rest.split('@', 1)[1]
                # 重建URL，不包含凭证
                repo_url = f"{protocol}://{host_path}"
                current_app.logger.info("已清除URL中的现有凭证")
        
        # 如果未提供目标文件夹路径，则尝试从URL提取
        if not project_folder:
            # 从URL中提取用户名和仓库名
            match = re.search(r'[:/]([^/]+)/([^/]+?)(?:\.git)?$', repo_url)
            if not match:
                current_app.logger.error("无法从URL中提取仓库信息")
                return jsonify({
                    "success": False,
                    "message": "无效的Git仓库URL",
                    "data": None
                })
                
            username, repo_name = match.groups()
            
            # 确保仓库名不包含.git后缀
            if repo_name.endswith('.git'):
                repo_name = repo_name[:-4]
            
            # 使用默认的项目根目录
            base_dir = os.environ.get('GIT_REPOS_BASE_DIR', 'G:\\')
            project_folder = os.path.join(base_dir, repo_name)
            current_app.logger.info(f"未提供目标路径，将使用: {project_folder}")
            
        # 确保目标目录存在
        os.makedirs(os.path.dirname(project_folder), exist_ok=True)
        
        # 检查目标路径是否被占用
        try:
            if os.path.exists(project_folder):
                # 尝试创建一个临时文件测试目录可写性
                test_file = os.path.join(project_folder, '.access_test')
                with open(test_file, 'w') as f:
                    f.write('test')
                os.remove(test_file)
                current_app.logger.info(f"目标路径 {project_folder} 可写")
        except Exception as e:
            current_app.logger.error(f"目标路径被占用或无法访问: {str(e)}")
            return jsonify({
                "success": False,
                "message": f"目标文件夹 {project_folder} 被其他程序占用或无法访问，请关闭相关程序后重试",
                "data": None
            })
        
        # 执行克隆操作
        current_app.logger.info(f"开始执行克隆操作...")
        clone_result = GitService.git_clone(
            repo_url=repo_url, 
            target_path=project_folder,
            project_id=project_id,
            credentials=credentials
        )
        current_app.logger.info(f"克隆操作完成: {clone_result['success']}")
        
        return jsonify(clone_result)
    except Exception as e:
        current_app.logger.error(f"Git克隆操作失败: {str(e)}")
        traceback.print_exc()
        return jsonify({
            "success": False,
            "message": f"Git克隆异常: {str(e)}",
            "data": None
        }) 